unit MLPAmain;

interface

uses
  Windows, Messages, SysUtils, Classes, Variants, Graphics, Controls, Forms, Dialogs,
  Menus, ClipBrd, Grids, ComCtrls, ExtCtrls, DBCtrls,  DBGrids,
  StdCtrls,DB, ADODB,{ShellAPI,} Math, FloatMatrix, Mask, MLPAOptions;
const
  GenoTyperFirstLineKit = 'File Name'+#9+'Dye'+#9+'Sample Info'+#9+'Category'+#9+'Label 1'+#9+'Label 2'+#9+'Label 3'+#9+'Number of Labels'+#9+'Gel File Name'+#9+'Overflow'+#9+'Kit';
  GenoTyperFirstLineNoKit = 'File Name'+#9+'Dye'+#9+'Sample Info'+#9+'Category'+#9+'Label 1'+#9+'Label 2'+#9+'Label 3'+#9+'Number of Labels'+#9+'Gel File Name'+#9+'Overflow';
  // Datacolumns in genotyperfiles
  FileNameCol = 0;
  DyeCol = 1;
  SampleInfoCol = 2;
  CategoryCol = 3;
  DataBasesCol = 4;
  DataHeightCol = 5;
  DataSurfaceCol = 6;
  GelFileNameCol = 8;
  OverflowCol = 9;
  KitCol = 10;

  // Description columns in sgAnalyses
  asgAssayId=0;
  asgRunDescr=1;
  asgRunDate=2;
  asgLabId=3;
  asgNormalTumor=4;
  asgTumorPercentage=5;
  asgNrGain=6;
  asgNrLoss=7;

  asgStartQC=8; // should be updated when colums are added in first part
  asgNrQScoreSummary=3; // number of summary quality scores
  QScoreSummaryNames:array[0..Pred(asgNrQScoreSummary)] of string = ('Q1','Q2','Q3');
  QScoreSummaryDescription:array[0..Pred(asgNrQScoreSummary)] of string = ('Ligase/LowDNA ratio','Median raw intensity','Long/short ratio');
  MedianPolishReps = 1;

type
  TStringObjectlist = Class(TStringlist)
    public
    procedure Delete(Index: Integer);override;
    procedure Clear;override;
  end;

  TfrmMain = class(TForm)
    btnABI310Samplefile: TButton;
    btnABI3700Samplefile: TButton;
    btnAddAnalysis: TButton;
    btnAddAssays: TButton;
    btnAnalysisAuthorize: TButton;
    btnAnalysisPrintReport: TButton;
    btnEditAnalysis: TButton;
    btnExportRunProtocol: TButton;
    btnImportMRCkit: TButton;
    btnLGTCOrderform: TButton;
    btnPrintRunProtocol: TButton;
    btnRetriveProbeInfo: TButton;
    cbAnalysisAvgReplicates: TCheckBox;
    cbShowQCProbes: TCheckBox;
    dbgAnalyses: TDBGrid;
    dbgKitAliases: TDBGrid;
    dbgKitProbes: TDBGrid;
    dbgKits: TDBGrid;
    dbgRunAssays: TDBGrid;
    dbgRuns: TDBGrid;
    dbgSampleAssays: TDBGrid;
    dbgSamples: TDBGrid;
    dlgColor: TColorDialog;
    dlgOpen: TOpenDialog;
    dlgPrint: TPrintDialog;
    dlgSave: TSaveDialog;
    edtAnalysisGain: TMaskEdit;
    edtAnalysisLoss: TMaskEdit;
    gbTresholds: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    MainMenu: TMainMenu;
    memTestGroups: TMemo;
    miAnalysisControlProbe: TMenuItem;
    miAnalysisPlotSample: TMenuItem;
    miAnalysisReinsertprobe: TMenuItem;
    miAnalysisRemoveassay: TMenuItem;
    miAnalysisRemoveprobe: TMenuItem;
    miAnalysisTestGroups: TMenuItem;
    miCloseDatabase: TMenuItem;
    miCopy: TMenuItem;
    miCut: TMenuItem;
    miEdit: TMenuItem;
    miFile: TMenuItem;
    miImportGenemapper: TMenuItem;
    miImportGenotyper: TMenuItem;
    miImportKit: TMenuItem;
    miNewDatabase: TMenuItem;
    miOpenDatabase: TMenuItem;
    miOptions: TMenuItem;
    miPaste: TMenuItem;
    miSelectRun: TMenuItem;
    miShowAbout: TMenuItem;
    miShowHelp: TMenuItem;
    miShowVignette: TMenuItem;
    miTools: TMenuItem;
    navKitProbes: TDBNavigator;
    navKits: TDBNavigator;
    navRuns: TDBNavigator;
    navSamples: TDBNavigator;
    pcMain: TPageControl;
    pnlAnalisisAuthorized: TPanel;
    pnlAnalyses: TPanel;
    pnlAnalysesAssays: TPanel;
    pnlAnalysisColorGain: TPanel;
    pnlAnalysisColorLoss: TPanel;
    pnlKitsBottom: TPanel;
    pnlKitsTop: TPanel;
    pnlRunAssays: TPanel;
    pnlRuns: TPanel;
    pnlSampleAssays: TPanel;
    pnlSamples: TPanel;
    pumAnalysisGrid: TPopupMenu;
    rgAnalysisProbeSortOrder: TRadioGroup;
    sbStatus: TStatusBar;
    sgAnalyses: TStringGrid;
    splAnalysesTab: TSplitter;
    splKitsTab: TSplitter;
    splRunsTab: TSplitter;
    splSamplesTab: TSplitter;
    tsAnalyses: TTabSheet;
    tsKits: TTabSheet;
    tsRuns: TTabSheet;
    tsSamples: TTabSheet;
    Label8: TLabel;
    lblRawdataType: TLabel;
    procedure btnAddAnalysisClick(Sender: TObject);
    procedure btnAddAssaysClick(Sender: TObject);
    procedure btnAuthorizeAnalysisClick(Sender: TObject);
    procedure btnEditAnalysisClick(Sender: TObject);
    procedure btnLGTCOrderformClick(Sender: TObject);
    procedure cbShowQCProbesClick(Sender: TObject);
    procedure edtAnalysisGainExit(Sender: TObject);
    procedure edtAnalysisLossExit(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure miImportGenotyperClick(Sender: TObject);
    procedure miOpenDatabaseClick(Sender: TObject);
    procedure miSelectRunClick(Sender: TObject);
    procedure sgAnalysesDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure miAnalysisControlProbeClick(Sender: TObject);
    procedure sgAnalysesMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure miAnalysisRemoveprobeClick(Sender: TObject);
    procedure pnlAnalysisColorLossClick(Sender: TObject);
    procedure rgAnalysisProbeSortOrderClick(Sender: TObject);
    procedure btnAnalysisPrintReportClick(Sender: TObject);
    procedure miOptionsClick(Sender: TObject);
    procedure miAnalysisPlotSampleClick(Sender: TObject);
    procedure miAnalysisReinsertprobeClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure miAnalysisRemoveassayClick(Sender: TObject);
    procedure btnRetriveProbeInfoClick(Sender: TObject);
    procedure pumAnalysisGridPopup(Sender: TObject);
    procedure miCopyClick(Sender: TObject);
    procedure btnExportRunProtocolClick(Sender: TObject);
    procedure btnImportMRCkitClick(Sender: TObject);
    procedure miImportGenemapperClick(Sender: TObject);
    procedure btnPrintRunProtocolClick(Sender: TObject);
    procedure dbgSamplesTitleClick(Column: TColumn);
    procedure dbgSampleAssaysTitleClick(Column: TColumn);
    procedure dbgRunsTitleClick(Column: TColumn);
    procedure dbgRunAssaysTitleClick(Column: TColumn);
    procedure dbgAnalysesTitleClick(Column: TColumn);
    procedure dbgKitProbesTitleClick(Column: TColumn);
    procedure dbgKitsTitleClick(Column: TColumn);
    procedure dbgKitAliasesTitleClick(Column: TColumn);
    procedure miShowAboutClick(Sender: TObject);
  private
    calibrationColumns : TLogVector;
    calibrationProbes:TStringList;
    columnmedians : TFloatVector;
    GroupProbes : TStringList;
    ExcludedProbes:TStringList;
    AssayIds : TIntVector;
    FAnalysisAuthorized: boolean;
    FAnalysisId : integer;
    FAnalysisModified : boolean;
    FColorHigh: TColor;
    FColorLow: TColor;
    FMouseCol : integer;
    FMouseRow : integer;
    FMouseRowClicked : integer;
    FTresholdHigh : double;
    FTresholdLow : double;
    normalrows : TlogVector;
    nrAssays :integer;
    qcfound : boolean;
    QCList : TStringObjectList;
    QCValues : TFloatMatrix;
    TestGroupColumns : TIntVector;
    rawvalues : TFloatmatrix;
    referenceColumns : TLogVector;
    referenceProbes: TStringList;
    rowmedians : TFloatVector;
    rsltvalues : TStringMatrix;
    transformedvalues : TFloatMatrix;
    FRawDataType: TRawDataType;


    procedure CalculateAnalysis;
    procedure CalculateQualityScores;
    procedure CalculateSampleAverages;
    procedure ExcludeProbes;
    procedure ReadAnalysisData;
    procedure ReadAuthorizedAnalysisData;
    procedure ShowAnalysisStringGrid;
    procedure SetAnalysisAuthorized(const Value: boolean);
    procedure SetColorHigh(const Value: TColor);
    procedure SetColorLow(const Value: TColor);
    procedure SetStatusMsg(index: integer; const Value: string);
    procedure SetTresholdHigh(const Value: double);
    procedure SetTresholdLow(const Value: double);
    procedure SortProbes;
    procedure tblAnalysesBeforeScroll(DataSet: TDataSet);
    procedure tblAnalysesAfterScroll(DataSet: TDataSet);
    procedure miTestgroupClick(Sender: TObject);
    function MV36toChrPos(MV36:string):string;
    procedure InitializeAnalysisData;
    procedure ReadAssayData;
    procedure ReadAssayProbeData;
    procedure ReadAnalysisProbeData;
    procedure SaveAnalysisSettings;
    procedure SetRawDataType(const Value: TRawDataType);
  public
    { Public declarations }
    dataList : TStringObjectlist;
    startData : integer;
    ProbeSortIndex : TIntVector;
    QCScore  : TFloatMatrix;
    function GetAnalysisColor(acol,arow:integer;StartColor:TColor):TColor;
    procedure DMcreated;
    property AnalysisAuthorized : boolean read FAnalysisAuthorized write SetAnalysisAuthorized;
    property ColorHigh : TColor read FColorHigh write SetColorHigh;
    property ColorLow : TColor read FColorLow write SetColorLow;
    property RawDataType : TRawDataType  read FRawDataType write SetRawDataType;
    property StatusMsg [index:integer] : string write SetStatusMsg;
    property TresholdHigh : double read FTresholdHigh write SetTresholdHigh;
    property TresholdLow : double read FTresholdLow write SetTresholdLow;
  end;

var
  frmMain: TfrmMain;

implementation

uses MLPAdm, MPLARunSelect, MLPAAssaySelect, MLPAEditAnalysis, MLPAAddAssay,
  MLPASelectSampleInfo, IEEE754,
  MLPArepAnalysis, MLPAShowSamples, MLPAReinsertProbes, MLPAGetProbeInfo,
  MLPARunReport, MLPAabout;

{$R *.DFM}

type
  TStringArray = array of String;

function Split(const Source, Delimiter: String): TStringArray;
var
  iCount:     Integer;
  iPos:       Integer;
  iLength:    Integer;
  sTemp:      String;
  aSplit:     TStringArray;

begin
  sTemp   := Source;
  iCount  := 0;
  iLength := Length(Delimiter) - 1;

  repeat
    iPos := Pos(Delimiter, sTemp);

    if iPos = 0 then
      break
    else begin
      Inc(iCount);
      SetLength(aSplit, iCount);
      aSplit[iCount - 1] := Copy(sTemp, 1, iPos - 1);
      Delete(sTemp, 1, iPos + iLength);
    end;
  until False;

  if Length(sTemp) > 0 then begin
    Inc(iCount);
    SetLength(aSplit, iCount);
    aSplit[iCount - 1] := sTemp;
  end;

  Result := aSplit;
end;

procedure TfrmMain.miImportGenemapperClick(Sender: TObject);
var
  DataFile: TStringList;
  DataValues : Array of TStringArray;
  DataIndex : Array of Integer;
  fields:TStringArray;
  i : integer;
  FilenamePrev : string;
  SampleinfoPrev : string;
  KitidPrev : string;
  probeid:string;
  ProbeCount : integer;
  AssayFileCount : integer;
  AssayDBCount : integer;
  AssayDataValid : boolean;
  runid : integer;
  assayid : integer;
  kitid : string;
  AssaySampleinfoCount : integer;
  AssayEmptySampleinfoCount : integer;
  //
  samplefilefield:integer;
  samplenamefield:integer;
  sampleidfield:integer;
  runnamefield:integer;
  panelfield:integer;
  markerfield:integer;
  dyefield:integer;
  allele1field:integer;
  size1field:integer;
  height1field:integer;
  peakarea1field:integer;


  function AssayInfoAcceptable(line:string;var runid:integer;var kitid:string):boolean;
  begin
    result:=true;
    assayid:=StrToInt(copy(line,pos('Assay#',line)+6,10));
    if DM.qrySampleInfo.Locate('assayid',assayid,[])then
    begin
      if runid=-1 then
        runid:= DM.qrySampleInfo.FieldByName('runid').AsInteger
      else
        if runid<> DM.qrySampleInfo.FieldByName('runid').AsInteger then
        begin
          result:=false;
          ShowMessage('All selected assays should be in the same run');
        end;
      kitid:= DM.qrySampleInfo.FieldByName('kitid').AsString;
    end;
  end;

  function getFieldIdx(fieldname:string):integer;
  var
    i:integer;
  begin
    i:=low(fields);
    while (i<=High(fields)) and (fields[i]<>fieldname) do
      inc(i);
    if i<=high(fields) then
      result:=i
    else
      result:=-1;
  end;

  function Smaller(i1,i2:integer):boolean;
  begin
    if DataValues[i1,FileNameCol]<DataValues[i2,FileNameCol] then
      result:=True
    else
      result:= (DataValues[i1,FileNameCol]=DataValues[i2,FileNameCol]) and
              (DataValues[i1,CategoryCol]<DataValues[i2,CategoryCol]);
  end;

  function Bigger(i1,i2:integer):boolean;
  begin
    if DataValues[i1,FileNameCol]>DataValues[i2,FileNameCol] then
      result:=True
    else
      result:= (DataValues[i1,FileNameCol]=DataValues[i2,FileNameCol]) and
              (DataValues[i1,CategoryCol]>DataValues[i2,CategoryCol]);
  end;


  procedure QuickSort(iLo, iHi: Integer) ;
  var
    Lo, Hi, Pivot, T: Integer;
  begin
    Lo := iLo;
    if iHi=27 then
      Hi:=iHi
    else
      Hi := iHi;
    Pivot := DataIndex[(Lo + Hi) div 2];
    repeat
      while (Lo<(probecount-1)) and Smaller(DataIndex[Lo],Pivot) do Inc(Lo) ;
      while (Hi>0) and Bigger(DataIndex[Hi],Pivot) do Dec(Hi) ;
      if Lo <= Hi then
      begin
        T := DataIndex[Lo];
        DataIndex[Lo] := DataIndex[Hi];
        DataIndex[Hi] := T;
        Inc(Lo) ;
        Dec(Hi) ;
      end;
    until Lo > Hi;
    if Hi > iLo then QuickSort(iLo, Hi) ;
    if Lo < iHi then QuickSort(Lo, iHi) ;
  end;

begin
  dlgOpen.Filter:='Text files (Tab separated)|*.txt|All files|*.*';
  dlgOpen.DefaultExt:='txt';
  dlgOpen.Title:='Import Genemapper datafile';
  If dlgOpen.Execute then
  begin
    StatusMsg[0]:='Importing data';
    DataFile:=TStringList.Create;
    try
      StatusMsg[1]:='Load datafile';
      DataFile.LoadFromFile(dlgOpen.FileName);
      // Check file format (headings/fieldnames)
      fields:=Split(DataFile[0],#9);
      samplefilefield:=GetFieldIdx('Sample File');
      samplenamefield:=GetFieldIdx('Sample Name');
      sampleidfield:=GetFieldIdx('Sample ID');
      runnamefield:=GetFieldIdx('Run Name');
      panelfield:=GetFieldIdx('Panel');
      markerfield:=GetFieldIdx('Marker');
      dyefield:=getFieldIdx('Dye');
      allele1field:=GetFieldIdx('Allele 1');
      size1field:=GetFieldIdx('Size 1');
      height1field:=GetFieldIdx('Height 1');
      peakarea1field:=GetFieldIdx('Peak Area 1');

      If (samplefilefield=-1) or (samplenamefield=-1) or (sampleidfield=-1) or
         (runnamefield=-1) or (panelfield=-1) or (markerfield=-1) or (dyefield=-1) or
         (allele1field=-1) or (size1field=-1) or (height1field=-1) or (peakarea1field=-1) then
      begin
        ShowMessage('Make sure that the selected file is exported by Genemapper');
        exit;
      end;
      ProbeCount:=DataFile.Count-1;
      SetLength(DataValues,ProbeCount);
      for i:=1 to DataFile.Count-1 do
      begin
        fields:=Split(DataFile[i],#9);
        Setlength(DataValues[i-1],KitCol+1);
        DataValues[i-1,FileNameCol]:=Fields[samplefilefield];
        DataValues[i-1,DyeCol]:=Fields[dyefield];
        DataValues[i-1,SampleInfoCol]:=Fields[samplenamefield];
        DataValues[i-1,CategoryCol]:=Fields[markerfield];
        DataValues[i-1, DataBasesCol]:=Fields[size1field];
        DataValues[i-1, DataHeightCol]:=Fields[height1field];
        DataValues[i-1, DataSurfaceCol]:=Fields[peakarea1field];
        DataValues[i-1, GelFileNameCol]:=Fields[runnamefield];
        DataValues[i-1, OverflowCol]:=Fields[sampleidfield];
        DataValues[i-1, KitCol]:=dm.KitAlias2ID(Fields[panelfield]);
      end;
      // check validity of data
      // count number of samples
      // file name and sampleifo should change at same time
      // kitid can change but should be known in kit database
      // probe ids should be available for the kits used

      // Make Index that orders SampleFile, Marker
      SetLength(DataIndex,ProbeCount);
      for i := 0 to ProbeCount - 1 do
        DataIndex[i]:=i;
      QuickSort(0,ProbeCount-1);

      AssayFileCount:=0;
      FilenamePrev:='';
      SampleinfoPrev:='';
      KitidPrev:='';
      StatusMsg[1]:='Checking datafile';
      runid:=-1;
      assayid:=-1;
      for i in DataIndex do
      begin
        if FilenamePrev<>DataValues[i,FileNameCol] then
        begin
          if SampleinfoPrev=DataValues[i,SampleInfoCol] then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Different filenames should have different sample name');
            exit;
          end;
          kitid:='';
          // see if assay can be identified from sample info
          //  should have no probe data in database
          //  if run id is determined already, it should be same
          DM.qrySampleInfo.Parameters[0].Value:=DataValues[i,SampleInfoCol];
          DM.qrySampleInfo.Open;
          AssaySampleinfoCount:=0;
          AssayEmptySampleinfoCount:=0;
          DataFile.Clear;
          while not DM.qrySampleInfo.Eof do
          begin
            Inc(AssaySampleinfoCount);
            if DM.qrySampleInfo.FieldByName('Probes').AsInteger=0 then
            begin
              if RunId=DM.qrySampleInfo.FieldByName('RunId').AsInteger then
              begin
                DataFile.Clear;
                DataFile.Add(DataValues[i,SampleInfoCol]+', Date:'+DM.qrySampleInfo.FieldByName('Date').AsString+', Kit: '+DM.qrySampleInfo.FieldByName('kitid').AsString+', Assay#'+DM.qrySampleInfo.FieldByName('assayid').AsString);
                AssayEmptySampleinfoCount:=1;
                Break;
              end
              else begin
                Inc(AssayEmptySampleinfoCount);
                DataFile.Add(DataValues[i,SampleInfoCol]+', Date:'+DM.qrySampleInfo.FieldByName('Date').AsString+', Kit: '+DM.qrySampleInfo.FieldByName('kitid').AsString+', Assay#'+DM.qrySampleInfo.FieldByName('assayid').AsString);
              end;
            end;
            DM.qrySampleInfo.Next;
          end;
          case AssayEmptySampleinfoCount of
          0:begin
            if AssaySampleinfoCount>0 then
            begin
              if MessageDlg('SampleInfo found but data has been entered to the database already. Do you want to continue ?',mtConfirmation,[mbYes,mbNo],0)=mrNo then
              begin
                exit;
              end;
            end;
          end;
          1:begin
            if not AssayInfoAcceptable(DataFile[0],runid,kitid) then
              exit;
          end;
          else
            assayid:=dlgSelectSampleInfo.SelectAssay(DataFile);
            if assayid>=0 then
              if not AssayInfoAcceptable(DataFile[assayid],runid,kitid) then
                exit;
          end;

          DM.qrySampleInfo.Close;
          // if assay is known already (kitid<>''), kitid in file should be identical
          if kitid<>'' then
            if DataValues[i,KitCol]<>kitid then
            begin
              ShowMessage('Kitid from assay is different from kitid in datafile, line '+IntToStr(i+2));
              exit;
            end;

          if KitidPrev<>DataValues[i,KitCol] then
          begin
            if Not DM.KitExists(DataValues[i,KitCol]) then
            begin
              ShowMessage('Kitid "'+DataValues[i,KitCol]+'" in the datafile is not in the kit database');
              exit;
            end;
          end;

          Inc(AssayFileCount);
          StatusMsg[1]:='Checking datafile, sample '+IntToStr(AssayFileCount);
        end
        else begin
          if SampleinfoPrev<>DataValues[i,SampleInfoCol] then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Different sample info should have different filename');
            exit;
          end;
          if KitidPrev<>DataValues[i,KitCol] then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. KitIds should be same for all probes in a sample');
            exit;
          end;
        end;
        // translate markerlength to probeid
        // check if probe id (category) exists for this kitid
        probeid:=DM.MarkerLengthToId(DataValues[i,KitCol],DataValues[i,CategoryCol]);
        if probeid='' then
        begin
          ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Kit '+DataValues[i,KitCol]+' does not contain a probe with length '+DataValues[i,CategoryCol]);
          exit;
        end;
        DataValues[i,CategoryCol]:=probeid;
        //
        FilenamePrev:=DataValues[i,FileNameCol];
        SampleinfoPrev:=DataValues[i,SampleInfoCol];
        KitidPrev:=DataValues[i,KitCol];
      end;
      // Check runs table for GelFilename.
      // After this section the current record in the tblRuns will be used
      StatusMsg[1]:='Check datarun';
      if (runid>=0) or DM.tblRuns.Locate('GelfileName',DataValues[0,GelFileNameCol],[]) then
      begin
        if runid>=0 then
          DM.tblRuns.Locate('runid',runid,[]);
        // see if there are data already
        AssayDBCount:=DM.AssayCount(DM.tblRuns.FieldValues['runid']);
        if AssayDBCount<>AssayFileCount Then
        begin
          // overwrite or append or cancel
          case MessageDlg('There are '+IntToStr(AssayDBCount)+' samples for this run in the database and '+IntToStr(AssayFileCount)+
                          ' samples in the file. Do you want to add the data to this run ? '+
                          '(Press No to add to another run, Cancel to stop processing the file)',
                          mtConfirmation,mbYesNoCancel,0) of
          mrNo:begin
            // ask for name of new run
            runid:=dlgSelectRun.SelectRun;
            if runid>=0 then
              DM.tblRuns.Locate('runid',runid,[])
            else
              exit;
          end;

          mrCancel:begin
            exit;
          end;
          end;
        end;
      end
      else begin
        // Ask if new run should be created
        case MessageDlg('This run does not seem to be registered already. Do you want to select an existing run ?'+
                      '(Press No to create a new run, Cancel to stop processing the file)',
                      mtConfirmation,mbYesNoCancel,0) of
        mrYes:begin
          // display dialog to choose run
          runid:=dlgSelectRun.SelectRun;
          if runid>=0 then
            DM.tblRuns.Locate('runid',runid,[])
          else
            exit;
        end;
        mrNo: begin
          DM.AddRun(DataValues[0,GelFileNameCol]); //  newly inserted will be current record
          ShowMessage('New Run inserted. Please update the fields on the Runs tab after the file is processed');
        end;
        mrCancel:begin
          exit;
        end;
        end;
      end;
      runid:=DM.tblRuns.FieldValues['runid'];

      // if we've got this far data can be inserted in the database
      FilenamePrev:='';
      SampleinfoPrev:='';
      KitidPrev:='';
      AssayDataValid:=false;
      DM.tblAssays.Open;
      DM.tblSamples.Active:=true;
      DM.connMLPAexp.BeginTrans;
      try
        for i in DataIndex do
        begin
          if FilenamePrev<>DataValues[i,FileNameCol] then
          begin
            StatusMsg[1]:='Import to database, probe '+IntToStr(i);
            // See if a sample/assay can be identified
            // check assays in current run
            kitid:=DataValues[i,KitCol];
            AssayDataValid:=true;
            if not DM.tblAssays.Locate('runid;SampleIDField',VarArrayOf([runid,DataValues[i,SampleInfoCol]]),[]) then
            begin
              if frmMLPAOptions.ImportAutoCreateAssays then
              begin
                assayid:=dlgSelectAssay.CreateAssay(kitid,runid,DataValues[i,SampleInfoCol],DataValues[i,FileNameCol],'');
              end
              else begin
                // show selection dialog,
                assayid:=dlgSelectAssay.SelectAssay(kitid,runid,DataValues[i,SampleInfoCol],DataValues[i,FileNameCol]);
              end;
              if assayid>=0 then
              begin
                DM.tblAssays.Locate('assayid',assayid,[]);
              end
              else
                AssayDataValid:=false;

            end;
            if AssayDataValid then
            begin
              // If it has data, ask to overwrite or skip
              // delete/update analysis samples
              assayid:=DM.tblAssays.FieldValues['assayid'];
            end;
          end
          else begin
          end;
          if AssayDataValid then
          begin
            // Put data in database
            DM.tblAssayProbes.InsertRecord([nil,assayid,DataValues[i,DyeCol],DataValues[i,CategoryCol],
                                            DataValues[i,DataBasesCol],DataValues[i,DataHeightCol],
                                            DataValues[i,DataSurfaceCol],DataValues[i,OverflowCol]]);
          end;
          FilenamePrev:=DataValues[i,FileNameCol];
          SampleinfoPrev:=DataValues[i,SampleInfoCol];
          KitidPrev:=DataValues[i,KitCol];
        end;
        //TODO: Check if there are assays in the run w/o probes, get some comment on reason why not in datafile


        DM.connMLPAexp.CommitTrans;
      except
        DM.connMLPAexp.RollbackTrans;

      end;
    finally
      DataFile.Free;
      StatusMsg[0]:='';
      StatusMsg[1]:='';
    end;
  end;
end;

procedure TfrmMain.miImportGenotyperClick(Sender: TObject);
var
  DataFile: TStringList;
  DataValues : Array of TStringArray;
  i : integer;
  FilenamePrev : string;
  SampleinfoPrev : string;
  KitidPrev : string;
  ProbeCount : integer;
  AssayFileCount : integer;
  AssayDBCount : integer;
  AssayDataValid : boolean;
  runid : integer;
  assayid : integer;
  kitid : string;
  filehaskit : boolean;
  AssaySampleinfoCount : integer;
  AssayEmptySampleinfoCount : integer;

  function AssayInfoAcceptable(line:string;var runid:integer;kitid:string):boolean;
  begin
    result:=true;
    assayid:=StrToInt(copy(line,pos('Assay#',line)+6,10));
    if DM.qrySampleInfo.Locate('assayid',assayid,[])then
    begin
      if runid=-1 then
        runid:= DM.qrySampleInfo.FieldByName('runid').AsInteger
      else
        if runid<> DM.qrySampleInfo.FieldByName('runid').AsInteger then
        begin
          result:=false;
          ShowMessage('All selected assays should be in the same run');
        end;
      kitid:= DM.qrySampleInfo.FieldByName('kitid').AsString;
    end;
  end;

begin
  dlgOpen.Filter:='Excel files|*.xls|Text files (Tab separated)|*.txt|All files|*.*';
  dlgOpen.DefaultExt:='xls';
  dlgOpen.Title:='Import Genotyper datafile';
  If dlgOpen.Execute then
  begin
    StatusMsg[0]:='Importing data';
    DataFile:=TStringList.Create;
    try
      StatusMsg[1]:='Load datafile';
      DataFile.LoadFromFile(dlgOpen.FileName);
      // Check file format (headings/fieldnames)
      If DataFile[0]=GenoTyperFirstLineKit then
      begin
        filehaskit:=true;
      end
      else if DataFile[0]=GenoTyperFirstLineNoKit then
      begin
        filehaskit:=false;
      end
      else begin
        ShowMessage('Make sure that the selected file is exported by Genotyper');
        exit;
      end;
      SetLength(DataValues,DataFile.Count-1);
      for i:=1 to DataFile.Count-1 do
      begin
        DataValues[i-1]:=Split(DataFile[i],#9);
        if not FileHasKit then
          Setlength(DataValues[i-1],KitCol+1);
      end;
      ProbeCount:=DataFile.Count-1;
      // check validity of data
      // count number of samples
      // file name and sampleifo should change at same time
      // kitid can change but should be known in kit database
      // probe ids should be available for the kits used
      AssayFileCount:=0;
      FilenamePrev:='';
      SampleinfoPrev:='';
      KitidPrev:='';
      StatusMsg[1]:='Checking datafile';
      runid:=-1;
      assayid:=-1;
      for i:=0 to ProbeCount-1 do
      begin
        if FilenamePrev<>DataValues[i,FileNameCol] then
        begin
          if SampleinfoPrev=DataValues[i,SampleInfoCol] then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Different filenames should have different sample info');
            exit;
          end;
          kitid:='';
          // see if assay can be identified from sample info
          //  should have no probe data in database
          //  if run id is determined already, it should be same
          DM.qrySampleInfo.Parameters[0].Value:=DataValues[i,SampleInfoCol];
          DM.qrySampleInfo.Open;
          AssaySampleinfoCount:=0;
          AssayEmptySampleinfoCount:=0;
          DataFile.Clear;
          while not DM.qrySampleInfo.Eof do
          begin
            Inc(AssaySampleinfoCount);
            if DM.qrySampleInfo.FieldByName('Probes').AsInteger=0 then
            begin
              if RunId=DM.qrySampleInfo.FieldByName('RunId').AsInteger then
              begin
                DataFile.Clear;
                DataFile.Add(DataValues[i,SampleInfoCol]+', Date:'+DM.qrySampleInfo.FieldByName('Date').AsString+', Kit: '+DM.qrySampleInfo.FieldByName('kitid').AsString+', Assay#'+DM.qrySampleInfo.FieldByName('assayid').AsString);
                AssayEmptySampleinfoCount:=1;
                Break;
              end
              else begin
                Inc(AssayEmptySampleinfoCount);
                DataFile.Add(DataValues[i,SampleInfoCol]+', Date:'+DM.qrySampleInfo.FieldByName('Date').AsString+', Kit: '+DM.qrySampleInfo.FieldByName('kitid').AsString+', Assay#'+DM.qrySampleInfo.FieldByName('assayid').AsString);
              end;
            end;
            DM.qrySampleInfo.Next;
          end;
          case AssayEmptySampleinfoCount of
          0:begin
            if AssaySampleinfoCount>0 then
            begin
              if MessageDlg('SampleInfo found but data has been entered to the database already. Do you want to continue ?',mtConfirmation,[mbYes,mbNo],0)=mrNo then
              begin
                exit;
              end;
            end;
          end;
          1:begin
            if not AssayInfoAcceptable(DataFile[0],runid,kitid) then
              exit;
          end;
          else
            assayid:=dlgSelectSampleInfo.SelectAssay(DataFile);
            if assayid>=0 then
              if not AssayInfoAcceptable(DataFile[assayid],runid,kitid) then
                exit;
          end;

          DM.qrySampleInfo.Close;
          if filehaskit then
          begin
            // if assay is known already (kitid<>-1), kitid in file should be identical
            if kitid<>'' then
              if DataValues[i,KitCol]<>kitid then
              begin
                ShowMessage('Kitid from assay is different from kitid in datafile, line '+IntToStr(i+2));
                exit;
              end;
            if KitidPrev<>DataValues[i,KitCol] then
            begin
              if Not DM.KitExists(DataValues[i,KitCol]) then
              begin
                ShowMessage('Kitid "'+DataValues[i,KitCol]+'" in the datafile is not in the kit database');
                exit;
              end;
            end;

          end;
          Inc(AssayFileCount);
          StatusMsg[1]:='Checking datafile, sample '+IntToStr(AssayFileCount);
        end
        else begin
          if SampleinfoPrev<>DataValues[i,SampleInfoCol] then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Different sample info should have different filename');
            exit;
          end;
          if filehaskit then
          begin
            if KitidPrev<>DataValues[i,KitCol] then
            begin
              ShowMessage('Error in datafile line '+IntToStr(i+2)+'. KitIds should be same for all probes in a sample');
              exit;
            end;
          end;
        end;
        // check if probe id (category) exists for this kitid
        if filehaskit then
        begin
         if not DM.GenotyperProbeExists(DataValues[i,KitCol],DataValues[i,CategoryCol]) then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Kit '+DataValues[i,KitCol]+' does not contain a probe '+DataValues[i,CategoryCol]);
            exit;
          end;
        end
        else if kitid<>'' then
        begin
         if not DM.GenotyperProbeExists(kitid,DataValues[i,CategoryCol]) then
          begin
            ShowMessage('Error in datafile line '+IntToStr(i+2)+'. Kit '+DataValues[i,KitCol]+' does not contain a probe '+DataValues[i,CategoryCol]);
            exit;
          end;
        end;

        FilenamePrev:=DataValues[i,FileNameCol];
        SampleinfoPrev:=DataValues[i,SampleInfoCol];
        if filehaskit then
          KitidPrev:=DataValues[i,KitCol];
      end;
      // Check runs table for GelFilename.
      // After this section the current record in the tblRuns will be used
      StatusMsg[1]:='Check datarun';
      if (runid>=0) or DM.tblRuns.Locate('GelfileName',DataValues[0,GelFileNameCol],[]) then
      begin
        if runid>=0 then
          DM.tblRuns.Locate('runid',runid,[]);
        // see if there are data already
        AssayDBCount:=DM.AssayCount(DM.tblRuns.FieldValues['runid']);
        if AssayDBCount<>AssayFileCount Then
        begin
          // overwrite or append or cancel
          case MessageDlg('There are '+IntToStr(AssayDBCount)+' samples for this run in the database and '+IntToStr(AssayFileCount)+
                          ' samples in the file. Do you want to add the data to this run ? '+
                          '(Press No to add to another run, Cancel to stop processing the file)',
                          mtConfirmation,mbYesNoCancel,0) of
          mrNo:begin
            // ask for name of new run
            runid:=dlgSelectRun.SelectRun;
            if runid>=0 then
              DM.tblRuns.Locate('runid',runid,[])
            else
              exit;
          end;

          mrCancel:begin
            exit;
          end;
          end;
        end;
      end
      else begin
        // Ask if new run should be created
        case MessageDlg('This run does not seem to be registered already. Do you want to select an existing run ?'+
                      '(Press No to create a new run, Cancel to stop processing the file)',
                      mtConfirmation,mbYesNoCancel,0) of
        mrYes:begin
          // display dialog to choose run
          runid:=dlgSelectRun.SelectRun;
          if runid>=0 then
            DM.tblRuns.Locate('runid',runid,[])
          else
            exit;
        end;
        mrNo: begin
          DM.AddRun(DataValues[0,GelFileNameCol]); //  newly inserted will be current record
          ShowMessage('New Run inserted. Please update the fields on the Runs tab after the file is processed');
        end;
        mrCancel:begin
          exit;
        end;
        end;
      end;
      runid:=DM.tblRuns.FieldValues['runid'];

      // if we've got this far data can be inserted in the database
      FilenamePrev:='';
      SampleinfoPrev:='';
      KitidPrev:='';
      AssayDataValid:=false;
      DM.tblAssays.Open;
      DM.connMLPAexp.BeginTrans;
      try
        for i:=0 to ProbeCount-1 do
        begin
          if FilenamePrev<>DataValues[i,FileNameCol] then
          begin
            StatusMsg[1]:='Import to database, probe '+IntToStr(i);
            // See if a sample/assay can be identified
            // check assays in current run
            if filehaskit then
              kitid:=DataValues[i,KitCol]
            else
              kitid:='';
            AssayDataValid:=true;
            if not DM.tblAssays.Locate('runid;SampleIDField',VarArrayOf([runid,DataValues[i,SampleInfoCol]]),[]) then
            begin
              // show selection dialog,
              assayid:=dlgSelectAssay.SelectAssay(kitid,runid,DataValues[i,SampleInfoCol],DataValues[i,FileNameCol]);
              if assayid>=0 then
              begin
                DM.tblAssays.Locate('assayid',assayid,[]);
              end
              else
                AssayDataValid:=false;
            end;
            if AssayDataValid then
            begin
              // If it has data, ask to overwrite or skip
              // delete/update analysis samples
              assayid:=DM.tblAssays.FieldValues['assayid'];
            end;
          end
          else begin
          end;
          if AssayDataValid then
          begin
            // Put data in database
            DM.tblAssayProbes.InsertRecord([nil,assayid,DataValues[i,DyeCol],DataValues[i,CategoryCol],
                                            DataValues[i,DataBasesCol],DataValues[i,DataHeightCol],
                                            DataValues[i,DataSurfaceCol],DataValues[i,OverflowCol]]);
          end;
          FilenamePrev:=DataValues[i,FileNameCol];
          SampleinfoPrev:=DataValues[i,SampleInfoCol];
          KitidPrev:=DataValues[i,KitCol];
        end;
        //TODO: Check if there are assays in the run w/o probes, get some comment on reason why not in datafile


        DM.connMLPAexp.CommitTrans;
      except
        DM.connMLPAexp.RollbackTrans;

      end;
    finally
      DataFile.Free;
      StatusMsg[0]:='';
      StatusMsg[1]:='';
    end;
  end;
end;

procedure TfrmMain.miSelectRunClick(Sender: TObject);
begin
  dlgSelectRun.SelectRun;
end;

procedure TfrmMain.SetStatusMsg(index: integer; const Value: string);
begin
  sbStatus.Panels[index].Text:=Value;
  sbStatus.Update;
end;

procedure TfrmMain.miShowAboutClick(Sender: TObject);
begin
  aboutBox.ShowModal;
end;

procedure TfrmMain.btnAddAnalysisClick(Sender: TObject);
begin
  dlgAnalysisEdit.AddAnalysis;
  // Somehow in ReadAssayData the query isn't properly updated the first time it is opened
  // Open it now to show all the data later on
  DM.qryAnalysisAssays.Open;
  DM.qryAnalysisAssays.Close;
  tblAnalysesAfterScroll(DM.tblAnalyses);
end;

procedure TfrmMain.SetColorHigh(const Value: TColor);
begin
  FColorHigh := Value;
  pnlAnalysisColorGain.Color:=value;
end;

procedure TfrmMain.SetColorLow(const Value: TColor);
begin
  FColorLow := Value;
  pnlAnalysisColorLoss.Color:=value;
end;

procedure TfrmMain.SetRawDataType(const Value: TRawDataType);
begin
  lblRawdataType.Caption:=rdtNames[Value];
  FRawDataType := Value;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  ColorLow:=clYellow;
  ColorHigh:=clAqua;
  fAnalysisId:=-1;
  startdata:=100;
  QCList:=TStringObjectList.Create;
  dataList:=TStringObjectList.Create;
  ExcludedProbes:=TStringList.Create;
  ExcludedProbes.Sorted:=true;
  referenceProbes:=TStringList.Create;
  referenceProbes.Sorted:=true;
  calibrationProbes:=TStringList.Create;
  calibrationProbes.Sorted:=true;
  GroupProbes:=TStringList.Create;
  // Cells property not available at design time
  sgAnalyses.Cells[asgAssayId,0]:='Assayid';
  sgAnalyses.Cells[asgRunDescr,0]:='RunProtocolName';
  sgAnalyses.Cells[asgRunDate,0]:='Date';
  sgAnalyses.ColWidths[asgRunDate]:=64;
  sgAnalyses.Cells[asgLabId,0]:='Labid';
  sgAnalyses.Cells[asgNormalTumor,0]:='n/t';
  sgAnalyses.ColWidths[asgNormalTumor]:=20;
  sgAnalyses.Cells[asgTumorPercentage,0]:='tum%';
  sgAnalyses.ColWidths[asgTumorPercentage]:=38;
  sgAnalyses.Cells[asgNrGain,0]:='gain';
  sgAnalyses.ColWidths[asgNrGain]:=24;
  sgAnalyses.Cells[asgNrLoss,0]:='loss';
  sgAnalyses.ColWidths[asgNrLoss]:=24;
  // Update asgStartQC constant when adding columns
end;

procedure TfrmMain.btnEditAnalysisClick(Sender: TObject);
begin
  dlgAnalysisEdit.EditAnalysis(DM.tblAnalyses.FieldValues['analysisid']);
end;

procedure TfrmMain.DMcreated;
begin
  DM.tblAnalyses.AfterScroll:=tblAnalysesAfterScroll;
  DM.tblAnalyses.BeforeScroll:=tblAnalysesBeforeScroll;
  // fill QC list
  // DM.GetProbeNummerlist('QC',QClist);
  if DM.tblAnalyses.Active then
    tblAnalysesAfterScroll(DM.tblAnalyses);
end;

procedure TfrmMain.SaveAnalysisSettings;
var
  i: Integer;
  strGroupProbes: string;
  bitvalue: Integer;
  acol: Integer;
  probes: TStringList;
begin
  DM.tblAnalyses.Edit;
  DM.tblAnalyses.FieldByName('tresholdlow').AsFloat := FTresholdLow;
  DM.tblAnalyses.FieldByName('tresholdhigh').AsFloat := FTresholdHigh;
  DM.tblAnalyses.FieldByName('colorlow').AsInteger := Integer(FColorlow);
  DM.tblAnalyses.FieldByName('colorhigh').AsInteger := Integer(FColorHigh);
  DM.tblAnalyses.FieldByName('excludedprobes').AsString := ExcludedProbes.CommaText;
  DM.tblAnalyses.FieldByName('controlprobes').AsString := referenceProbes.CommaText;
  DM.tblAnalyses.FieldByName('stableprobes').AsString := calibrationProbes.CommaText;
  DM.tblAnalyses.FieldByName('authorized').AsBoolean := AnalysisAuthorized;
  DM.tblAnalyses.FieldByName('rawdatatype').AsInteger:=Integer(frmMLPAOptions.AnalysisRawDataType);
  DM.tblAnalyses.FieldByName('usecontrolprobes').AsBoolean:=frmMLPAOptions.AnalysisUseControlProbes;
  DM.tblAnalyses.FieldByName('usecontrolsamples').AsBoolean:=frmMLPAOptions.AnalysisUseControlSamples;
  DM.tblAnalyses.FieldByName('medianpolish').AsInteger:=frmMLPAOptions.AnalysisMedPolish;

  GroupProbes.Assign(memTestGroups.Lines);
  if GroupProbes.Count > 0 then
  begin
    probes := TStringlist.Create;
    for i := 0 to Pred(GroupProbes.Count) do
    begin
      bitvalue := 1 shl i;
      probes.Clear;
      for acol := 0 to Pred(dataList.Count) do
        if (TestGroupColumns[acol] and bitvalue) = bitvalue then
          probes.add(datalist[acol]);
      GroupProbes[i] := GroupProbes[i] + ':' + probes.CommaText;
    end;
    probes.free;
    strGroupProbes := GroupProbes[0];
    for i := 1 to pred(GroupProbes.Count) do
      strGroupProbes := strGroupProbes + '|' + GroupProbes[i];
  end
  else
    strGroupProbes := '';
  DM.tblAnalyses.FieldByName('groupprobes').AsString := strGroupProbes;
  DM.tblAnalyses.Post;
end;

procedure TfrmMain.ReadAssayData;
var
  arow: Integer;
begin
  // data
  DM.qryAnalysisAssays.Open;
  arow := 1;
  while not DM.qryAnalysisAssays.Eof do
  begin
    StatusMsg[1] := 'Loading sample ' + IntToStr(arow);
    AssayIds[arow - 1] := DM.qryAnalysisAssays.FieldValues['AnalysisAssayId'];
    sgAnalyses.Cells[asgAssayId, arow] := DM.qryAnalysisAssays.FieldValues['SampleIdField'];
    sgAnalyses.Cells[asgRunDescr, arow] := DM.qryAnalysisAssays.FieldValues['Gelfilename'];
    sgAnalyses.Cells[asgRunDate, arow] := DM.qryAnalysisAssays.FieldValues['Date'];
    sgAnalyses.Cells[asgLabId, arow] := DM.qryAnalysisAssays.FieldValues['LabId'];
    sgAnalyses.Cells[asgNormalTumor, arow] := DM.qryAnalysisAssays.FieldValues['normaaltumor'];
    if sgAnalyses.Cells[asgNormalTumor, arow] = 'N' then
      normalrows[arow - 1] := True
    else
      normalrows[arow - 1] := False;
    sgAnalyses.Cells[asgTumorPercentage, arow] := DM.qryAnalysisAssays.FieldValues['tumorpercentage'];
    sgAnalyses.Cells[asgNrGain, arow] := '0';
    sgAnalyses.Cells[asgNrLoss, arow] := '0';
    inc(arow);
    DM.qryAnalysisAssays.Next;
  end;
  DM.qryAnalysisAssays.Close;
end;

procedure TfrmMain.ReadAssayProbeData;
var
  nummer: string;
  acol: Integer;
  arow: Integer;
  rawvalue: Double;
const
  rawdatafield: array[PeakHeight..PeakArea] of string = ('Label2', 'Label3');
begin
  // data
  DM.qryAnalysisAssays.Open;
  arow := 1;
  while not DM.qryAnalysisAssays.Eof do
  begin
    DM.qryAssayProbes.Parameters[0].Value := DM.qryAnalysisAssays.FieldValues['assayid'];
    DM.qryAssayProbes.Open;
    while not DM.qryAssayProbes.Eof do
    begin
      rawvalue := DM.qryAssayProbes.FieldByName(rawdatafield[frmMLPAOptions.AnalysisRawDataType]).AsFloat;
      nummer := DM.qryAssayProbes.FieldValues['category'];
      acol := QCList.IndexOf(nummer);
      if acol >= 0 then
      begin
        qcfound := true;
        if rawvalue > 0 then
          QCValues[acol, arow - 1] := rawvalue
        else
          QCValues[acol, arow - 1] := Nan;
      end
      else
      begin
        acol := dataList.IndexOf(nummer);
        if acol >= 0 then
        begin
          if rawvalue > 0 then
            rawValues[acol, arow - 1] := rawvalue
          else
            rawValues[acol, arow - 1] := Nan;
        end;
      end;
      DM.qryAssayProbes.Next;
    end;
    DM.qryAssayProbes.Close;
    inc(arow);
    DM.qryAnalysisAssays.Next;
  end;
  DM.qryAnalysisAssays.Close;
end;

procedure TfrmMain.ReadAuthorizedAnalysisData;
begin
  StatusMsg[0]:='Loading data';
  FAnalysisModified:=false;
  fAnalysisId:=DM.tblAnalyses.FieldValues['analysisid'];
  nrAssays:=DM.getAnalysisAssayCount(fAnalysisId);
  if nrAssays=0 then
  begin
    sgAnalyses.Visible:=false;
  end
  else begin
    StatusMsg[1]:='Probe metadata';
    InitializeAnalysisData;
    RawDataType:=TRawDataType(DM.tblAnalyses.FieldByName('rawdatatype').AsInteger);
    ReadAssayData;
    ReadAnalysisProbeData;
    CalculateQualityScores;
  end;
  StatusMsg[0]:='';
  StatusMsg[1]:='';
  CalculateSampleAverages;
  SortProbes;
  ShowAnalysisStringGrid;
end;

procedure TfrmMain.InitializeAnalysisData;
var
  bitvalue: Integer;
  probes: TStringList;
  rsltvalue: string;
  acol: Integer;
  arow: Integer;
  i: Integer;
begin
  sgAnalyses.Visible := true;
  sgAnalyses.RowCount := nrAssays + 1;
  DM.GetProbeIDList(DM.tblAnalyses.FieldValues['kitid'], datalist, QCList);
  // descriptive colnames
  ExcludedProbes.CommaText := DM.tblAnalyses.FieldByName('excludedprobes').AsString;
  referenceProbes.CommaText := DM.tblAnalyses.FieldByName('controlprobes').AsString;
  calibrationProbes.CommaText := DM.tblAnalyses.FieldByName('stableprobes').AsString;
  rsltValue := DM.tblAnalyses.FieldByName('groupprobes').AsString;
  ExtractStrings(['|'], [], pChar(rsltValue), GroupProbes);
  ColorLow := TColor(DM.tblAnalyses.FieldByName('colorlow').AsInteger);
  ColorHigh := TColor(DM.tblAnalyses.FieldByName('colorhigh').AsInteger);
  TresholdLow := DM.tblAnalyses.FieldByName('tresholdlow').AsFloat;
  TresholdHigh := DM.tblAnalyses.FieldByName('tresholdhigh').AsFloat;
  AnalysisAuthorized := DM.tblAnalyses.FieldByName('authorized').AsBoolean;
  SetLength(AssayIds, nrAssays);
  SetLength(rawvalues, datalist.count, nrAssays);
  SetLength(transformedvalues, datalist.count, nrAssays);
  SetLength(rsltvalues, datalist.count, nrAssays);
  SetLength(rowmedians, nrAssays);
  Setlength(columnmedians, datalist.count);
  Setlength(referenceColumns, datalist.count);
  SetLength(calibrationColumns, datalist.count);
  Setlength(normalrows, nrAssays);
  Setlength(QCValues, QClist.count, nrAssays);
  SetLength(QCScore, asgNrQScoreSummary, nrAssays);
  SetLength(TestGroupColumns, datalist.count);
  // prepare datavalues
  for acol := 0 to datalist.count - 1 do
  begin
    TestGroupColumns[acol] := 0;
    referenceColumns[acol]:=referenceProbes.IndexOf(datalist[acol]) >= 0;
    calibrationColumns[acol] :=calibrationProbes.IndexOf(datalist[acol]) >= 0;
    for arow := 0 to nrAssays - 1 do
    begin
      // emptyvalues[acol,arow]:=True;
      rawvalues[acol, arow] := Nan;
      transformedvalues[acol, arow] := 0;
      rsltvalues[acol, arow] := 'unknown';
    end;
  end;
  qcfound := false;
  for acol := 0 to QCList.Count - 1 do
  begin
    for arow := 0 to nrAssays - 1 do
    begin
      QCvalues[acol, arow] := Nan;
    end;
  end;
  probes := TStringList.Create;
  memTestGroups.Clear;
  for arow := 0 to Pred(GroupProbes.Count) do
  begin
    memTestGroups.Lines.Add(Copy(GroupProbes[arow], 1, Pos(':', GroupProbes[arow]) - 1));
    probes.CommaText := Copy(GroupProbes[arow], Pos(':', GroupProbes[arow]) + 1, 200);
    bitvalue := 1;
    for i := 0 to Pred(probes.count) do
    begin
      acol := Datalist.IndexOf(probes[i]);
      if acol >= 0 then
        TestGroupColumns[acol] := TestGroupColumns[acol] + bitvalue;
      bitvalue := bitvalue shl 1;
    end;
  end;
  probes.free;
end;

procedure TfrmMain.tblAnalysesAfterScroll(DataSet: TDataSet);

begin
  // fill stringgrid
  If Dataset.FieldValues['analysisid']<>fAnalysisId then
  begin
    if Dataset.FieldValues['analysisid']<>Null then
    begin
      if Dataset.FieldValues['authorized'] then
      begin
        ReadAuthorizedAnalysisData;
      end
      else begin
        ReadAnalysisData;
        if nrAssays>0 then
        begin
          ExcludeProbes;
          CalculateAnalysis;
          sgAnalyses.Visible:=true;
        end;
      end;
    end
    else begin
      sgAnalyses.Visible:=false;
    end;
  end;
end;

procedure TfrmMain.sgAnalysesDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  curColor : TColor;

  procedure ChangeBackGround(aColor:TColor);
  begin
    sgAnalyses.Canvas.Brush.Color:=aColor;
    FillRect(sgAnalyses.Canvas.Handle, Rect, sgAnalyses.Canvas.Brush.Handle);
    sgAnalyses.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, sgAnalyses.Cells[ACol, ARow]);
  end;

begin
  if (acol>=startdata) and (arow>0) then
  begin
    curColor:=GetAnalysisColor(acol-startdata,arow-1, sgAnalyses.Canvas.Brush.Color);
    if curColor<>sgAnalyses.Canvas.Brush.Color then
      ChangeBackGround(curColor);
  end;
  inherited;
end;



procedure TfrmMain.CalculateAnalysis;
var
  values : TFloatVector;
  refvalues : TFloatMatrix;
  colmeans : TFloatVector;
  refindexcol : TIntVector;
  sortidx:TIntVector;
  arow : integer;
  acol : integer;
  i : integer;

begin
  CalculateQualityScores;
  Setlength(values,max(datalist.count,nrAssays));
  // Copy rawvalues (dimension+NAN)
  transformedvalues:=MatrixCopy(rawvalues);
  // multiplicative median polish of all intensity values after removing excluded probes
  for i:=1 to frmMLPAOptions.AnalysisMedPolish do
  begin
    // normalize by sample median
    rowmedians:=Median(TransformedValues,2);
    for arow :=0 to nrAssays-1 do
      for acol:=0 to datalist.count-1 do
        transformedvalues[acol,arow]:=transformedvalues[acol,arow] / rowmedians[arow];
    // normalize by probe median
    columnmedians:=Median(transformedValues,1);
    for acol:=0 to datalist.count-1 do
      for arow:=0 to nrAssays-1 do
        transformedvalues[acol,arow]:=transformedvalues[acol,arow] / columnmedians[acol];
  end;
  // Select probes from controlprobes that are most stable in normal samples (closest to 1)
  //
  if (Log2IntVector(referenceColumns)<>nil) and frmMLPAOptions.AnalysisUseControlProbes then
    refvalues:=SelectCols(transformedvalues,referenceColumns)
  else
    refvalues:=MatrixCopy(transformedvalues); // no designated ref probes, use all
  if (Log2IntVector(normalrows)<>nil) and frmMLPAOptions.AnalysisUseControlSamples then
    refvalues:=SelectRows(refvalues,normalrows); // select normal samples if they are in analysis
  // normalize subset
  for i:=1 to frmMLPAOptions.AnalysisMedPolish do
  begin
    rowmedians:=Median(refvalues,2);
    for arow :=0 to High(refvalues[0]) do
      for acol:=0 to High(refvalues) do
        refvalues[acol,arow]:=refvalues[acol,arow] / rowmedians[arow];
    columnmedians:=Median(refvalues,1);
    for arow :=0 to High(refvalues[0]) do
      for acol:=0 to High(refvalues) do
        refvalues[acol,arow]:=refvalues[acol,arow] / columnmedians[acol];
  end;
  colmeans:=Mean(refvalues,1);
  for i:=0 to High(colmeans) do
    colmeans[i]:=abs(colmeans[i]-1); // lowest values will be closest to 1
  // sort means, keep indexes
  sortidx:=SortIndex(colmeans);
  // take lowest 5
  for i:=0 to datalist.Count-1 do
    calibrationColumns[i]:=false;
  calibrationProbes.Clear;
  SetLength(sortidx,min(frmMLPAOptions.AnalysisStableProbes,Length(SortIdx)));
  // translate indexes in controlprobes subset to all data
  if (Log2IntVector(referenceColumns)<>nil) and frmMLPAOptions.AnalysisUseControlProbes then
  begin // If it's NIL then no controlcolumns are designated-> no translation is needed
    refindexcol:=Log2IntVector(referenceColumns);
    for i:=0 to high(sortidx) do
    begin
      sortidx[i]:=refindexcol[sortidx[i]];
      calibrationColumns[sortidx[i]]:=true;
      calibrationProbes.Add(datalist[sortidx[i]]);
    end;
  end;
  // normalize all data by control samples and stable probes
  for i:=1 to frmMLPAOptions.AnalysisMedPolish do
  begin
    refvalues:=SelectCols(transformedvalues,sortidx); // select stable probes
    rowmedians:=Median(refvalues,2);
    for arow :=0 to nrAssays-1 do
      for acol:=0 to datalist.count-1 do
        transformedvalues[acol,arow]:=transformedvalues[acol,arow] / rowmedians[arow];
    if (Log2IntVector(normalrows)<>nil) and frmMLPAOptions.AnalysisUseControlSamples then
      refvalues:=SelectRows(transformedValues,normalrows)
    else
      refvalues:=MatrixCopy(transformedValues);
    columnmedians:=Median(refvalues,1);
    for acol:=0 to datalist.Count-1 do
      for arow:=0 to nrAssays-1 do
        transformedvalues[acol,arow]:=transformedvalues[acol,arow] / columnmedians[acol];
  end;
  CalculateSampleAverages;
  SortProbes;
  ShowAnalysisStringGrid;

end;

procedure TfrmMain.CalculateQualityScores;
var
  concmeans : TFloatVector;
  concindexcol : TIntVector;
  ligmeans :TFloatVector;
  ligindexcol :TIntVector;
  i,j: integer;
  acol: integer;
  halfsize : integer;

  procedure AddIndex(idx:integer;var indexcol:TIntVector);
  begin
    SetLength(indexcol,length(indexcol)+1);
    indexcol[high(indexcol)]:=i;
  end;

  procedure swap;
  var
    tmp:integer;
  begin
    tmp:=ligindexcol[j];
    ligindexcol[j]:=ligindexcol[i];
    ligindexcol[i]:=tmp;
  end;

begin
  // First Quality Score
  // - QCScore= Ligasecheck / Median(LowDNA)
  acol:=0;
  SetLength(concindexcol,0);
  SetLength(ligindexcol,0);
  for i:=0 to QClist.Count-1 do
  begin
    case TProbeInfo(QCList.Objects[i]).qctype of
      2:begin
        AddIndex(i,Concindexcol);
      end;
      3:begin
        AddIndex(i,ligindexcol);
      end;
    end;
  end;
  concmeans:=Median(SelectCols(QCValues,concindexcol),2);
  ligmeans:=Median(SelectCols(QCValues,ligindexcol),2);
  for i:=0 to pred(nrAssays) do
  begin
     // Do IsNan first to prevent invalid floating point operation
    if IsNan(concmeans[i]) or IsNan(ligmeans[i]) or (concmeans[i]=0) then
      QCScore[acol,i]:=NaN
    else
      QCScore[acol,i]:=ligmeans[i]/concmeans[i];
  end;
  // Second Quality score, median of raw datavalues
  acol:=1;
  if datalist.count>20 then
    concmeans:=Median(SelectCols(rawvalues,Seq(0,19)),2)
  else
    concmeans:=Median(rawvalues,2);
  for i:=0 to pred(nrAssays) do
  begin
    QCScore[acol,i]:=concmeans[i];
  end;
  // third Quality score, ratio of long probes/short probes
  acol:=2;
  // Make an index sorted by fragment length
  SetLength(ligindexcol,datalist.count);
  for i:=0 to datalist.count-1 do
    ligindexcol[i]:=i;
  for i:=0 to datalist.count-2 do
    for j:=i+1 to datalist.count-1 do
    begin
      if TProbeInfo(datalist.Objects[ligindexcol[i]]).length > TProbeInfo(datalist.Objects[ligindexcol[j]]).length then swap;
    end;

  halfsize:= datalist.count div 2;
  if halfsize=0 then
  begin
    for i:= 0 to pred(nrAssays) do
      QCScore[acol,i]:=NaN;
  end
  else begin
    SetLength(concindexcol,halfsize);
    for i:=0 to Pred(Halfsize) do
      concindexcol[i]:=ligindexcol[i];
    concmeans:=Median(SelectCols(rawvalues,concindexcol),2);
    for i:=0 to pred(nrAssays) do
    begin
      QCScore[acol,i]:=concmeans[i];
    end;
    for i:=0 to Pred(Halfsize) do
      concindexcol[i]:=ligindexcol[Pred(datalist.count)-i];
    concmeans:=Median(SelectCols(rawvalues,concindexcol),2);
    for i:= 0 to pred(nrAssays) do
    begin
      if IsNan(QCScore[acol,i]) or IsNan(concmeans[i]) or (QCScore[acol,i]=0) then
        QCScore[acol,i]:=NaN
      else
        QCScore[acol,i]:=concmeans[i]/QCScore[acol,i];
    end;
  end;
end;

procedure TfrmMain.CalculateSampleAverages;
begin

end;

procedure TfrmMain.btnAddAssaysClick(Sender: TObject);
begin
  dlgAddAssay.AddAssay('',DM.tblRuns.FieldByName('runid').AsInteger,'');
  DM.qryRunAssays.Requery;
end;

procedure TfrmMain.btnLGTCOrderformClick(Sender: TObject);
begin
  //ShellExecute(Handle, 'open', PChar('http://www.lgtc.nl/home/forms/full_plate.html'), nil, nil, SW_SHOW);
end;

procedure TfrmMain.btnPrintRunProtocolClick(Sender: TObject);
begin
  if Not Assigned(frmRunReport) then
    Application.CreateForm(TfrmRunReport,frmRunReport);
  frmRunReport.DoDialog;
end;

procedure TfrmMain.miOpenDatabaseClick(Sender: TObject);
begin
  dlgOpen.DefaultExt:='mdb';
  dlgOpen.Filter:='Acces files|*.mdb|All files|*.*';
  if dlgOpen.Execute then
  begin
    DM.ExpDatabaseMDB:=dlgOpen.FileName;
  end;
end;

procedure TfrmMain.cbShowQCProbesClick(Sender: TObject);
begin
  ShowAnalysisStringGrid;
end;


procedure TfrmMain.ShowAnalysisStringGrid;
var
  acol,arow, icol:integer;
  NrGain : TIntVector;
  NrLoss : TIntVector;
begin
  case cbShowQCProbes.State of
    cbChecked:if QCFound then
                startdata:=asgStartQC+QClist.Count+asgNrQScoreSummary
              else
                startdata:=asgStartQC+asgNrQScoreSummary;
    cbUnChecked:startdata:=asgStartQC;
    cbGrayed:startdata:=asgStartQC+asgNrQScoreSummary;
  end;
  // make decision
  for acol:=0 to datalist.Count-1 do
  begin
    for arow:=0 to nrAssays-1 do
    begin
      if IsNaN(transformedvalues[acol,arow]) then
        rsltvalues[acol,arow]:='empty'
      else if transformedvalues[acol,arow]<=FTresholdLow then
        rsltvalues[acol,arow]:='loss'
      else if transformedvalues[acol,arow]>=FTresholdHigh then
        rsltvalues[acol,arow]:='gain'
      else
        rsltvalues[acol,arow]:='';
    end;
  end;

  sgAnalyses.ColCount:=startdata+datalist.Count;
  Setlength(NrGain,nrAssays);
  Setlength(NrLoss,nrAssays);
  for arow:=0 to nrAssays-1 do
  begin
    NrGain[arow]:=0;
    nrLoss[arow]:=0;
  end;
  // qualitycontrol probes
  if (cbShowQCProbes.State=cbChecked) and qcFound then
  begin
    for acol:=0 to QClist.Count-1 do
    begin
      sgAnalyses.ColWidths[asgStartQC+acol]:=30;
      sgAnalyses.Cells[asgStartQC+acol,0]:=QClist[acol];
      for arow:=0 to nrAssays-1 do
      begin
        if isnan(QCvalues[acol,arow]) then
          sgAnalyses.Cells[asgStartQC+acol,arow+1]:=''
        else
          sgAnalyses.Cells[asgStartQC+acol,arow+1]:=FormatFloat('0.#',QCvalues[acol,arow])
      end;
    end;
  end;
  if cbShowQCProbes.State<>cbUnchecked then
  begin
    for acol:=0 to pred(asgNrQScoreSummary) do
    begin
      sgAnalyses.ColWidths[startdata-asgNrQScoreSummary+acol]:= 30;
      sgAnalyses.Cells[startdata-asgNrQScoreSummary+acol,0]:=QScoreSummaryNames[acol];
      for arow:=0 to nrAssays-1 do
      begin
        if isnan(QCscore[acol,arow]) then
          sgAnalyses.Cells[startdata-asgNrQScoreSummary+acol,arow+1]:=''
        else
          sgAnalyses.Cells[startdata-asgNrQScoreSummary+acol,arow+1]:=FormatFloat('0.#',QCscore[acol,arow])
      end;
    end;
  end;
  // prepare datavalues
  for acol:=0 to datalist.count-1 do
  begin
    icol:=ProbeSortIndex[acol];
    sgAnalyses.ColWidths[startdata+acol]:=30;
    if calibrationColumns[icol] then
    begin
      sgAnalyses.Cells[startdata+acol,0]:=datalist[icol]+'c';
    end
    else if referenceColumns[icol] then
    begin
      sgAnalyses.Cells[startdata+acol,0]:=datalist[icol]+'r';
    end
    else begin
      sgAnalyses.Cells[startdata+acol,0]:=datalist[icol];
    end;
    for arow:=0 to nrAssays-1 do
    begin
      if isNan(transformedvalues[icol,arow]) then
        sgAnalyses.Cells[startdata+acol,arow+1]:=''
      else
      begin
        if transformedvalues[icol,arow]<=FTresholdLow then
          inc(NrLoss[arow]);
        if transformedvalues[icol,arow]>=FTresholdHigh then
          inc(NrGain[arow]);

        sgAnalyses.Cells[startdata+acol,arow+1]:=FormatFloat('0.00',transformedvalues[icol,arow]);
      end;
    end;
  end;
  for arow:=0 to nrAssays-1 do
  begin
    sgAnalyses.Cells[asgNrGain,arow+1]:=IntToStr(NrGain[arow]);
    sgAnalyses.Cells[asgNrLoss,arow+1]:=IntToStr(NrLoss[arow]);
  end;
end;


procedure TfrmMain.SetAnalysisAuthorized(const Value: boolean);
begin
  FAnalysisAuthorized := Value;
  btnAnalysisAuthorize.Enabled:= not value;
  btnEditAnalysis.Enabled:=not value;
  pnlAnalisisAuthorized.Visible:=value;
  edtAnalysisLoss.Enabled:=not value;
  edtAnalysisGain.Enabled:=not value;
end;

procedure TfrmMain.SetTresholdHigh(const Value: double);
begin
  FTresholdHigh := Value;
  if (value<=1) then
    FTresholdHigh:=1.01;
  if (value>10) then
    FTresholdHigh:=10;
  edtAnalysisGain.Text:=FormatFloat('0.00',FTresholdHigh);
end;

procedure TfrmMain.SetTresholdLow(const Value: double);
begin
  FTresholdLow := Value;
  if (value>=1) then
    FTresholdLow:=0.99;
  if (value<0.1) then
    FTresholdLow:=0.1;
  edtAnalysisLoss.Text:=FormatFloat('0.00',FTresholdLow);
end;

procedure TfrmMain.edtAnalysisLossExit(Sender: TObject);
begin
  TresholdLow:=StrToFloat(edtAnalysisLoss.Text);
  ShowAnalysisStringGrid;
  sgAnalyses.Repaint;
  FAnalysisModified:=true;
end;

procedure TfrmMain.edtAnalysisGainExit(Sender: TObject);
begin
  TresholdHigh:=StrToFloat(edtAnalysisGain.Text);
  ShowAnalysisStringGrid;
  sgAnalyses.Repaint;
  FAnalysisModified:=true;
end;

procedure TfrmMain.btnAuthorizeAnalysisClick(Sender: TObject);
var
  arow: Integer;
  acol: Integer;
begin
  // Put transformed data in database and set authorized flag
  if MessageDlg('Are you sure? The settings for this analysis can not be changed after authorizing it.', mtConfirmation, mbYesNo,0)=mrYes then
  begin
    // TODO: check if already exists for this analysis and delete it
    for arow := 0 to Length(AssayIds) - 1 do
    begin
      for acol := 0 to DataList.Count - 1 do
        dm.tblAnalysisProbes.InsertRecord([nil,AssayIds[arow],Datalist[acol],rawvalues[acol,arow],transformedvalues[acol,arow],rsltvalues[acol,arow]]);
      for acol := 0 to QCList.Count - 1 do
        dm.tblAnalysisProbes.InsertRecord([nil,AssayIds[arow],QClist[acol],QCvalues[acol,arow]]);
    end;
    AnalysisAuthorized:=TRUE;
    SaveAnalysisSettings;
  end;
end;

procedure TfrmMain.miAnalysisControlProbeClick(Sender: TObject);
var
  ProbeId:String;
  acol:integer;
begin
  // check control status
  if Not AnalysisAuthorized then
  begin
    if (FMouseCol>=startdata) and (FMouseCol<startdata+datalist.count) then
    begin
      ProbeId:=datalist[ProbeSortIndex[FMouseCol-startdata]];
      if referenceProbes.IndexOf(ProbeId)>=0 then
      begin
        referenceProbes.Delete(referenceProbes.IndexOf(ProbeId));
      end
      else begin
        referenceProbes.Add(ProbeId);
      end;

      for acol:=0 to datalist.count-1 do
      begin
        if referenceProbes.IndexOf(datalist[acol])>=0 then
        begin
          referenceColumns[acol]:=true;
        end
        else begin
          referenceColumns[acol]:=false;
        end;
      end;
      FAnalysisModified:=true;
      CalculateAnalysis;
    end;
  end;
end;

procedure TfrmMain.miAnalysisRemoveprobeClick(Sender: TObject);
var
  ProbeId:String;
begin
  // subset all arrays with columns
  if Not AnalysisAuthorized then
  begin
    if (FMouseCol>=startdata) and (FMouseCol<startdata+datalist.count) then
    begin
      ProbeId:=datalist[ProbeSortIndex[FMouseCol-startdata]];
      ExcludedProbes.Add(ProbeId);
      FAnalysisModified:=true;
      ExcludeProbes;
      CalculateAnalysis;
    end;
  end;
end;

procedure TfrmMain.ReadAnalysisData;
begin
  StatusMsg[0]:='Loading data';
  FAnalysisModified:=false;
  fAnalysisId:=DM.tblAnalyses.FieldValues['analysisid'];
  nrAssays:=DM.getAnalysisAssayCount(fAnalysisId);
  if nrAssays=0 then
  begin
    sgAnalyses.Visible:=false;
  end
  else begin
    StatusMsg[1]:='Probe metadata';
    RawDataType:=frmMLPAOptions.AnalysisRawDataType;
    InitializeAnalysisData;
    ReadAssayData;
    ReadAssayProbeData;
  end;
  StatusMsg[0]:='';
  StatusMsg[1]:='';
end;

procedure TfrmMain.ReadAnalysisProbeData;
var
  nummer: string;
  acol: Integer;
  arow: Integer;
  rawvalue: Double;
  transformedvalue: Double;
  resultvalue: string;
begin
  ExcludeProbes;
  DM.qryAnalysisAssays.Open;
  arow := 1;
  while not DM.qryAnalysisAssays.Eof do
  begin
    DM.qryAnalysisProbes.Parameters[0].Value := DM.qryAnalysisAssays.FieldValues['analysisassayid'];
    DM.qryAnalysisProbes.Open;
    while not DM.qryAnalysisProbes.Eof do

    begin
      rawvalue := DM.qryAnalysisProbes.FieldByName('rawvalue').AsFloat;
      transformedvalue:=DM.qryAnalysisProbes.FieldByName('transformed').AsFloat;
      resultvalue:=DM.qryAnalysisProbes.FieldByName('result').AsString;
      nummer := DM.qryAnalysisProbes.FieldByName('category').AsString;
      acol := QCList.IndexOf(nummer);
      if acol >= 0 then
      begin
        qcfound := true;
        if rawvalue > 0 then
          QCValues[acol, arow - 1] := rawvalue
        else
          QCValues[acol, arow - 1] := Nan;
      end
      else
      begin
        acol := dataList.IndexOf(nummer);
        if acol >= 0 then
        begin
          if rawvalue > 0 then
          begin
            rawValues[acol, arow - 1] := rawvalue;
            transformedvalues[acol, arow-1] :=transformedvalue;
            rsltvalues[acol, arow-1]:=resultvalue;
          end
          else
            rawValues[acol, arow - 1] := Nan;
        end;
      end;
      DM.qryAnalysisProbes.Next;
    end;
    DM.qryAnalysisProbes.Close;
    inc(arow);
    DM.qryAnalysisAssays.Next;
  end;
  DM.qryAnalysisAssays.Close;
end;

procedure TfrmMain.tblAnalysesBeforeScroll(DataSet: TDataSet);
begin
  if FAnalysisModified and (not AnalysisAuthorized) then
  begin
    SaveAnalysisSettings;
  end;
end;

{ TStringObjectlist }

procedure TStringObjectlist.Clear;
var
  i:integer;
begin
  if Count>0 then
    for i:=0 to Count-1 do
      Objects[i].Free;
  inherited;
end;

procedure TStringObjectlist.Delete(Index: Integer);
begin
  if (Index < 0) or (Index >= Count) then Error('List index out of bounds (%d)', Index);
  Objects[Index].Free;
  inherited;
end;

procedure TfrmMain.sgAnalysesMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  acol,arow : integer;
  ProbeInfo:TProbeInfo;
begin
  sgAnalyses.MouseToCell(X,Y,acol,arow);
  if arow<>FMouseRow then
  begin
    FMouseRow:=arow;
  end;
  if acol<>FMouseCol then
  begin
    FMouseCol:=acol;
    // Fill Hint property
    if (Acol>=startdata) and (ACol<startdata+dataList.Count) then
      ProbeInfo:=TProbeInfo(datalist.Objects[ProbeSortIndex[ACol-startdata]])
    else if qcfound and (ACol>=asgStartQC) and
         (Acol<(asgStartQC+QCList.Count)) and (cbShowQCProbes.State=cbChecked) then
      ProbeInfo:=TProbeInfo(QCList.Objects[ACol-asgStartQC])
    else
      ProbeInfo:=nil;
    if Assigned(ProbeInfo) then
    begin
      sgAnalyses.Hint:='id: '+ProbeInfo.id+#13+
                       'Chrompos: '+ProbeInfo.cytoband+#13+
                       'Gene: '+ProbeInfo.gene;
    end
    else begin
      if (cbShowQCProbes.State<>cbUnchecked) and (Acol<startdata) and (Acol>=(startdata-asgNrQScoreSummary)) then
        sgAnalyses.Hint:=QScoreSummaryDescription[Acol-startdata+asgNrQScoreSummary]
      else
        sgAnalyses.Hint:='';
    end;
  end;
end;

procedure TfrmMain.ExcludeProbes;
var
  dataprobes : TLogVector;
  i:integer;
begin
  SetLength(Dataprobes,datalist.count);
  for i:=datalist.count-1 downto 0 do
    if ExcludedProbes.IndexOf(datalist[i])>=0 then
    begin
      dataprobes[i]:=false;
      datalist.Objects[i].Free;
      dataList.Objects[i]:=nil;
      datalist.Delete(i);
    end
    else
      dataprobes[i]:=true;

  rawvalues:=SelectCols(rawvalues,dataprobes);
  transformedvalues:=SelectCols(transformedvalues,dataprobes);
  rsltvalues:=SelectCols(rsltvalues,dataprobes);
  columnmedians:=SelectElements(columnmedians,dataprobes);
  referenceColumns:=SelectElements(referenceColumns,dataprobes);
  calibrationColumns:=SelectElements(calibrationColumns,dataprobes);
  TestGroupColumns:=SelectElements(TestGroupColumns,dataprobes);
  SortProbes;
end;

procedure TfrmMain.pnlAnalysisColorLossClick(Sender: TObject);
begin
  dlgColor.Color:=TPanel(Sender).Color;
  if dlgColor.Execute then
  begin
    if TPanel(Sender).Tag=1 Then
      ColorLow:=dlgColor.Color
    else if TPanel(Sender).Tag=2 then
      ColorHigh:=dlgColor.Color;
  end;
  // ShowAnalysisStringGrid;
  sgAnalyses.Repaint;
  FAnalysisModified:=true;
end;

procedure TfrmMain.SortProbes;
var
  i,j: integer;
  procedure swap;
  var
    tmp:integer;
  begin
    tmp:=ProbeSortIndex[j];
    ProbeSortIndex[j]:=ProbeSortIndex[i];
    ProbeSortIndex[i]:=tmp;
  end;

begin
  SetLength(ProbeSortIndex,datalist.count);
  for i:=0 to datalist.count-1 do
    ProbeSortIndex[i]:=i;
  for i:=0 to datalist.count-2 do
    for j:=i+1 to datalist.count-1 do
    begin
      case rgAnalysisProbeSortOrder.ItemIndex of
      0:if StrToInt(TProbeInfo(datalist.Objects[ProbeSortIndex[i]]).order) > StrToInt(TProbeInfo(datalist.Objects[ProbeSortIndex[j]]).order) then swap;
      1:if TProbeInfo(datalist.Objects[ProbeSortIndex[i]]).length > TProbeInfo(datalist.Objects[ProbeSortIndex[j]]).length then swap;
      2:if TProbeInfo(datalist.Objects[ProbeSortIndex[i]]).chrpos > TProbeInfo(datalist.Objects[ProbeSortIndex[j]]).chrpos then swap;
      3:if TProbeInfo(datalist.Objects[ProbeSortIndex[i]]).gene > TProbeInfo(datalist.Objects[ProbeSortIndex[j]]).gene then swap;
      4:if referenceColumns[ProbeSortIndex[i]] and not referenceColumns[ProbeSortIndex[j]] then swap;
      end;
    end;
end;

procedure TfrmMain.rgAnalysisProbeSortOrderClick(Sender: TObject);
begin
  SortProbes;
  ShowAnalysisStringGrid;
end;

procedure TfrmMain.btnAnalysisPrintReportClick(Sender: TObject);
begin
  dlgPrintAnalysis.ShowPreviewDialog;
end;

procedure TfrmMain.miOptionsClick(Sender: TObject);
begin
  frmMLPAOptions.ShowOptions;
  if AnalysisAuthorized then
  begin
    ShowMessage('The current analysis is authorized and can not be changed');
  end
  else begin
    if sgAnalyses.Visible then
    begin
      ReadAnalysisData;
      ExcludeProbes;
      CalculateAnalysis;
    end;
  end;
end;

function TfrmMain.GetAnalysisColor(acol, arow: integer;
  StartColor: TColor): TColor;
var
  icol : integer;

  type colors = packed record
    red,green,blue,pal : byte;
  end;

  function Darker(c:TColor; value:integer):tcolor;
  var
    col:colors;
  begin
    col:=colors(c);
    if (col.pal<=2) then with col do
    begin
      blue:=min(255,max(0,blue - value));
      green:=min(255,max(0,green - value));
      red:=min(255,max(0,red - value));
    end;
    result:=TColor(col);
  end;

begin
  icol:=ProbeSortIndex[acol];
  result:=StartColor;
  if rsltvalues[icol,arow]='gain' then
  begin
    result:=FColorHigh;
  end;
  if rsltvalues[icol,arow]='loss' then
  begin
    result:=FColorLow;
  end;
  if calibrationColumns[icol] then
  begin
    result:=Darker(result,40);
  end
  else if referenceColumns[icol] then
  begin
    result:=Darker(result,20);
  end;
end;

procedure TfrmMain.miAnalysisPlotSampleClick(Sender: TObject);
var
  labid:string;
  i: integer;
  nrsamples : integer;
  Samples: array of integer;
begin
  // find labid
  if (FMouseRowClicked>0) and (FMouseRowClicked<sgAnalyses.RowCount) then
  begin
    nrsamples:=0;
    labid:=sgAnalyses.Cells[asgLabid,FMouseRowClicked];
    for i:=1 to sgAnalyses.RowCount-1 do
    begin
      if sgAnalyses.Cells[asgLabid,i]=labId then
      begin
        inc(nrSamples);
        SetLength(Samples,NrSamples);
        Samples[NrSamples-1]:=i;
      end;
    end;
    dlgShowAnalysisSample.DoDialog(Samples);
  end;
end;

procedure TfrmMain.miAnalysisReinsertprobeClick(Sender: TObject);
var
  CompleteDatalist : TStringObjectlist;
  I: Integer;
begin
  if Not AnalysisAuthorized then
  begin
    CompleteDatalist:=TStringObjectlist.Create;
    DM.GetProbeIDList(DM.tblAnalyses.FieldValues['kitid'],completedatalist,nil);
    if dlgReinsertprobes.DoDialog(ExcludedProbes,completedatalist) then
    begin
      // save changes to database
      FAnalysisModified:=true;
      tblAnalysesBeforeScroll(DM.tblAnalyses);
      // reread data, excluded probes were deleted from data matrices
      ReadAnalysisData;
      ExcludeProbes;
      CalculateAnalysis;
    end;
    for I := 0 to completedataList.Count - 1 do
      CompleteDatalist.Objects[i].Free;
    CompleteDataList.Free;
  end;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  tblAnalysesBeforeScroll(DM.tblAnalyses);
end;

procedure TfrmMain.miAnalysisRemoveassayClick(Sender: TObject);
begin
  // Remove record from tblAnalysisAssays
  if Not AnalysisAuthorized then
  begin
    if dm.tblAnalysisAssays.Locate('analysisassayid',AssayIds[FMouseRow-1],[]) then
    begin
      dm.tblAnalysisAssays.Delete;
      ReadAnalysisData;
      ExcludeProbes;
      CalculateAnalysis;
    end;
  end;
end;

procedure TfrmMain.btnRetriveProbeInfoClick(Sender: TObject);
var
  c_probes,e_probes:string;
begin
  if Not AnalysisAuthorized then
  begin
    if dlgGetProbeInfo.DoDialog(DM.tblAnalyses.FieldValues['kitid'],c_probes,e_probes) then
    begin
      ExcludedProbes.CommaText:=e_probes;
      referenceProbes.CommaText:=c_probes;
      // save changes to database
      FAnalysisModified:=true;
      tblAnalysesBeforeScroll(DM.tblAnalyses);
      // reread data, excluded probes were deleted from data matrices
      ReadAnalysisData;
      ExcludeProbes;
      CalculateAnalysis;
    end;
  end;
end;

type
  AnalysisMenuItems = (amControlProbe,amRemoveProbe,amReinsertProbe,amPlotSample,amRemoveAssay,amTestGroups);

procedure TfrmMain.pumAnalysisGridPopup(Sender: TObject);
var
  i: integer;
  ProbeId:String;
  selectedcol : integer;
  EnabledMenu : Set of AnalysisMenuItems;
begin
  // Check TestGroupColumns
  EnabledMenu := [];
  if (FMouseCol>=startdata) and (FMouseCol<startdata+datalist.count) then
  begin
    selectedCol:=ProbeSortIndex[FMouseCol-startdata];
    ProbeId:=datalist[selectedcol];
    Include(EnabledMenu,amTestGroups);
    if Not AnalysisAuthorized then
    begin
      Include(EnabledMenu,amControlProbe);
      Include(EnabledMenu,amRemoveProbe);
    end;
    miAnalysisControlProbe.Checked:=referenceColumns[selectedcol];
    i:=0;
    while i<memTestGroups.Lines.Count do
    begin
      if i>=miAnalysisTestGroups.Count then
      begin
        miAnalysisTestGroups.Add(TMenuItem.Create(Self));
        miAnalysisTestGroups.Items[i].Tag:=i;
        miAnalysisTestGroups.Items[i].OnClick:=miTestGroupClick;
      end;
      miAnalysisTestGroups.Items[i].Caption:=memTestGroups.Lines[i];
      miAnalysisTestGroups.Items[i].Enabled:= Not AnalysisAuthorized;
      miAnalysisTestGroups.Items[i].Checked:=((TestGroupColumns[selectedcol] shr i) and 1) = 1 ;

      inc(i)
    end;
    while miAnalysisTestGroups.Count>i do
      miAnalysisTestGroups.Delete(i);
  end
  else begin
    miAnalysisControlProbe.Checked:=false;
  end;
  if (FMouseRow>0) and (FMouseRow<sgAnalyses.RowCount) then
  begin
    Include(EnabledMenu,amPlotSample);
    if Not AnalysisAuthorized then
      Include(EnabledMenu,amRemoveAssay);
  end;
  if not AnalysisAuthorized then
    Include(EnabledMenu,amReinsertProbe);
  FMouseRowClicked:=FMouseRow;
  miAnalysisControlProbe.Enabled:=amControlProbe in EnabledMenu;
  miAnalysisRemoveProbe.Enabled:=amRemoveProbe in EnabledMenu;
  miAnalysisReinsertProbe.Enabled:=amReinsertProbe in EnabledMenu;
  miAnalysisPlotSample.Enabled:=amPlotSample in EnabledMenu;
  miAnalysisRemoveAssay.Enabled:=amRemoveAssay in EnabledMenu;
  miAnalysisTestGroups.Enabled:=amTestGroups in EnabledMenu;
end;


procedure TfrmMain.miTestgroupClick(Sender: TObject);
  // add/remove probe from group
var
  selectedcol : integer;
  bitvalue: integer;
begin
  if (FMouseCol>=startdata) and (FMouseCol<startdata+datalist.count) then
  begin
    selectedCol:=ProbeSortIndex[FMouseCol-startdata];
    bitvalue:=1 shl TMenuItem(Sender).Tag;
    TestGroupColumns[selectedCol]:= TestGroupColumns[selectedcol] xor bitvalue;
  end;
end;

function TfrmMain.MV36toChrPos(MV36: string): string;
var
  position:double;
  basepos:string;
begin
  result:='';
  if length(MV36)<7 then
    exit;
  if ((MV36[1]='X') or (MV36[1]='Y')) and (MV36[2]='-') then
    MV36:=MV36[1]+' '+Copy(MV36,2,6);
  if (MV36[3]<>'-') or (MV36[7]<>'.') then
    exit;
  position:=StrToFloat(copy(MV36,4,5))*1e6;
  basepos:=Trim(Format('%9.0f',[position]));
  result:=copy(copy(MV36,1,2)+':0000000',1,12-length(basepos))+basepos;
end;

procedure TfrmMain.miCopyClick(Sender: TObject);

  procedure CopySGToClipboard(sg:TStringgrid);
  var
    i,j : integer;
    txt : string;
  begin
    txt:='';
    for i:=0 to sg.RowCount-1 do
    begin
      for j:=0 to sg.ColCount-1 do
      begin
        if j=0 then
          txt:=txt+sg.Cells[j,i]
        else
          txt:=txt+#9+sg.Cells[j,i];
      end;
      txt:=txt+#13+#10;
      Clipboard.AsText:=txt;
    end;
  end;

begin
  if pcMain.ActivePage=tsAnalyses then
  begin
    Clipboard.Open;
    Clipboard.Clear;
    CopySGToClipboard(sgAnalyses);
    Clipboard.Close;
  end;
end;

procedure TfrmMain.dbgAnalysesTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgAnalyses.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgAnalyses.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgAnalyses.Columns.Count) then
    begin
      dbgAnalyses.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgAnalyses.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgKitAliasesTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgKitAliases.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgKitAliases.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgKitAliases.Columns.Count) then
    begin
      dbgKitAliases.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgKitAliases.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgKitProbesTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgKitProbes.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgKitProbes.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgKitProbes.Columns.Count) then
    begin
      dbgKitProbes.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgKitProbes.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgKitsTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgKits.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgKits.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgKits.Columns.Count) then
    begin
      dbgKits.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgKits.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgRunAssaysTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgRunAssays.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgRunAssays.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgRunAssays.Columns.Count) then
    begin
      dbgRunAssays.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgRunAssays.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgRunsTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgRuns.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgRuns.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgRuns.Columns.Count) then
    begin
      dbgRuns.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgRuns.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgSampleAssaysTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgSampleAssays.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgSampleAssays.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgSampleAssays.Columns.Count) then
    begin
      dbgSampleAssays.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgSampleAssays.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName, string(Sort)) = 1)
    and (Pos(' DESC', string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.dbgSamplesTitleClick(Column: TColumn);
{$J+}
 const PreviousColumnIndex : integer = -1;
{$J-}
begin
  if dbgSamples.DataSource.DataSet is TCustomADODataSet then
  with TCustomADODataSet(dbgSamples.DataSource.DataSet) do
  begin
    if (0<=PreviousColumnIndex) and (PreviousColumnIndex<dbgSamples.Columns.Count) then
    begin
      dbgSamples.Columns[PreviousColumnIndex].title.Font.Style :=
      dbgSamples.Columns[PreviousColumnIndex].title.Font.Style - [fsBold];
    end;

    Column.title.Font.Style :=
    Column.title.Font.Style + [fsBold];
    PreviousColumnIndex := Column.Index;

    if (Pos(Column.Field.FieldName,string(Sort)) = 1)
    and (Pos(' DESC',string(Sort))= 0) then
      Sort := Column.Field.FieldName + ' DESC'
    else
      Sort := Column.Field.FieldName + ' ASC';
  end;
end;

procedure TfrmMain.btnExportRunProtocolClick(Sender: TObject);
var
  Lines:TStringList;
begin
  Lines:=TStringList.Create;
  Lines.Add('LabId'+chr(9)+'KitId'+chr(9)+'SampleInfo'+chr(9)+'Well'+chr(9)+'Comments');
  DM.qryRunExport.Open;
  while not DM.qryRunExport.Eof do
  begin
    Lines.Add(DM.qryRunExportLabId.Value+chr(9)+
              DM.qryRunExportkitid.Value+chr(9)+
              DM.qryRunExportSampleIDField.Value+chr(9)+
              DM.qryRunExportPlateWell.Value+chr(9)+
              DM.qryRunExportComment.Value);
    DM.qryRunExport.Next;
  end;
  DM.qryRunExport.Close;
  if dlgSave.Execute then
  begin
    Lines.SaveToFile(dlgSave.FileName);
  end;
  Lines.Free;
end;

procedure TfrmMain.btnImportMRCkitClick(Sender: TObject);
var
  txtfile:TStringlist;
  fields:TStringArray;
  controlprobes:TStringlist;
  kitname:string;
  seppos:integer;
  linenr:integer;
  FragmentLength:integer;
  gene,probe,genesub,chrpos:string;
  stopQC:boolean;
  QCprobes: TStringList;
  I: Integer;
begin
  dlgOpen.Filter:='Text files (Tab separated)|*.txt|All files|*.*';
  dlgOpen.DefaultExt:='txt';
  dlgOpen.Title:='Import MRC kit description file';
  If dlgOpen.Execute then
  begin
    txtfile:=TStringList.Create;
    controlprobes:=TStringList.Create;
    QCprobes:=TStringlist.Create;
    try
      txtfile.LoadFromFile(dlgOpen.FileName);
      // check validity
      if txtfile.Count<=2 then
      begin
        ShowMessage('File contains not enough lines of text');
        exit;
      end;
      // first line contains 'THE CONTROL PROBES' and 'Quality control Fragments'
      if ((Pos('THE CONTROL PROBES',txtfile[0])=0) and (Pos('THE REFERENCE PROBES',txtfile[0])=0)) or (Pos('Quality control Fragments',txtfile[0])=0) then
      begin
        ShowMessage('Unexpected first line in file, it should contain ''THE CONTROL PROBES'' and ''Quality control Fragments''');
        exit;
      end;
      // second line contains fieldnames
      fields:=Split(UpperCase(txtfile[1]),#9);
      if (length(fields)<12) or (copy(fields[4],1,11)<>'RECOMMENDED') or (copy(fields[3],1,2)<>'MV')
         or (fields[9]<>'GENE') or (Copy(fields[11],1,8)<>'FRAGMENT')then
      begin
        ShowMessage('The second line in the file does not contain the proper fields');
        exit;
      end;
      // first word is id, must be unique in kits table
      fields:=split(txtfile[0],#9);
      fields[0]:=Trim(fields[0]);
      seppos:=Pos(' ',fields[0]);
      kitname:=Copy(txtfile[0],1,seppos-1);
      if DM.tblKits.Locate('KitID',kitname,[]) then
      begin
        ShowMessage('There is already a kit named '''+kitname+''' in the kit list');
        exit;
      end;
      DM.connMLPAexp.BeginTrans;
      try
        stopQC:=false;
        DM.tblKits.AppendRecord([kitname,copy(fields[0],seppos+1,50),'MRC Holland']);
        for linenr := 2 to txtfile.Count - 1 do
        begin
          fields:=split(txtfile[linenr],#9);
          if Length(fields)>9 then
            controlprobes.Add(fields[9]);
          if (length(fields)>12) and (fields[11]<>'') and (TryStrToInt(fields[12],FragmentLength)) then
            QCprobes.AddObject(fields[11],TObject(FragmentLength));
          // extract genename and probename
          seppos:=pos(' probe ',fields[0]);
          if seppos>0 then
          begin
            gene:=Copy(fields[0],1,seppos-1);
            probe:=copy(fields[0],seppos+7,50);
          end
          else begin
            gene:=fields[0];
            probe:=fields[0];
          end;
          // extract within gene id and possibly chrpos
          chrpos:=MV36toChrPos(copy(fields[3],1,8));
          if chrpos='' then
          begin
            genesub:=fields[3];
            // convert chrpos from cytoband
          end
          else begin
            genesub:=copy(fields[3],10,50);
          end;
          if gene<>'' then
          begin
            DM.tblProbes.Append;
            DM.tblProbes.FieldByName('KitID').AsString:=kitname;
            DM.tblProbes.FieldByName('id').AsInteger:=linenr-1;
            DM.tblProbes.FieldByName('length').AsInteger:=StrToInt(fields[2]);
            DM.tblProbes.FieldByName('ProbeName').AsString:=fields[0];
            DM.tblProbes.FieldByName('cytoband').AsString:=fields[1];
            DM.tblProbes.FieldByName('chrpos').AsString:=chrpos;
            DM.tblProbes.FieldByName('order').AsInteger:=StrToInt(fields[4]);
            DM.tblProbes.FieldByName('control').AsBoolean:=controlprobes.IndexOf(fields[0])<>-1;
            DM.tblProbes.FieldByName('gene').AsString:=gene;
            DM.tblProbes.FieldByName('genesub').AsString:=genesub;
            DM.tblProbes.FieldByName('probe').AsString:=probe;
            DM.tblProbes.FieldByName('type').AsInteger:=1;
            DM.tblProbes.Post;
          end;
        end;
        for I := 0 to QCprobes.Count - 1 do
        begin
          DM.tblProbes.Append;
          DM.tblProbes.FieldByName('KitID').AsString:=kitname;
          DM.tblProbes.FieldByName('id').AsInteger:=101+i;
          DM.tblProbes.FieldByName('length').AsInteger:=integer(QCprobes.Objects[i]);
          DM.tblProbes.FieldByName('ProbeName').AsString:=QCprobes[i];
          DM.tblProbes.FieldByName('gene').AsString:=QCprobes[i];
          probe:=UpperCase(QCprobes[i]);
          if pos('CONC',probe)=1 then
            DM.tblProbes.FieldByName('type').AsInteger:=2
          else if pos('LIGATION',probe)=1 then
            DM.tblProbes.FieldByName('type').AsInteger:=3
          else
            DM.tblProbes.FieldByName('type').AsInteger:=4;
          DM.tblProbes.Post;
        end;
        DM.connMLPAexp.CommitTrans;
      except
        DM.connMLPAexp.RollbackTrans;
        raise;
      end;
    finally
      txtfile.Free;
      controlprobes.Free;
      QCprobes.Free;
    end;
  end;
end;

end.
